package modelo;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;

import javax.xml.parsers.ParserConfigurationException;

import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;



import constants.Constants;


/**
 * this class will handle all of the card loaded from XML
 * @author Jose Luis Garcia && Alvaro Blazquez Checa
 *
 */
public class Card {
	
	private List<Monje> monkList;
	private List<LibraryCard> libraryCards;
	private List<ScriptoriumCard> scriptoriumCards;
	private List<EventCards> eventsCards;
	private List<CryptaCard> criptaCards;
	
	
	/**
	 * this will instantiate the methods to read 
	 */
	public Card() {
		createMonjes();
		libraryCards = this.createLibraryCards();
		scriptoriumCards = this.createScriptoriumCards();
		eventsCards = this.createEventCards();
		criptaCards=this.createCriptaCards();
	}
	


	/**
	 * this will deal  to the player and mark them as a new suspect in their sheet
	 * @param playerList
	 */
	public void dealSuspectSheets(List<Player> playerList) {
		
		for (Player j : playerList) {
			Set<Suspect> suspects = new TreeSet<Suspect>();
			for (Monje monje : monkList) {
				suspects.add(new Suspect(monje));
			}
			j.setHojaDeSospechosos(suspects);
		}
		
	}		
	
	/**
	 * this will give players monks cards depends of the number of player 
	 * @param playerList
	 * @param deskCards
	 * @return
	 */
	public Monje dealMonkCards(List<Player> playerList, Set<Monje> deskCards) {
		List<Monje> monkListCopy = new ArrayList<Monje>(monkList);
		Monje murderer = null;
		if (monkListCopy.size() == 24) {

			Collections.shuffle(monkListCopy); // Barajar las cartas de Monje
			murderer = monkListCopy.remove(monkList.size()-1);
		
			
			switch (playerList.size()) {
			case 1:
				dealCards(playerList,10,monkListCopy);
				break;
			case 2:
				dealCards(playerList,8,monkListCopy);
				break;
			case 3:
				dealCards(playerList,6,monkListCopy);
				break;
			case 4:
				dealCards(playerList,5,monkListCopy);
				break;
			case 5:
				dealCards(playerList,4,monkListCopy);
				break;
			case 6:
				dealCards(playerList,3,monkListCopy);
			}// end switch;
			deskCards.addAll(monkListCopy);
		} 
		return murderer;
	}
	
	/**
	 * this method will deal the cards to the player
	 * @param playerList
	 * @param numCards
	 * @param monkListCopy
	 */
	private void dealCards(List<Player> playerList,int numCards,List<Monje> monkListCopy) {
		for(Player p : playerList){
			Set<Monje> monkSet = new TreeSet<Monje>();
			for(int i=1;i<=numCards;i++){
				monkSet.add(monkListCopy.remove(monkListCopy.size()-1));
			}
			p.setCartasMonje(monkSet);
			System.out.println("*********************");
			System.out.println(p.getPlayerName()+ " tiene " + monkSet);
			System.out.println("*********************");
		}
	}
	
	
	//<GETTERS>
	
	
	/**
	 * get a Library Cards
	 * @return 
	 */
	public List<LibraryCard> getLibraryCards() {
		return libraryCards;
	}
	
	/**
	 * get a Monk cards
	 * @return
	 */
	public List<Monje> getMonkList() {
		return monkList;
	}
	
	/**
	 * get event cards
	 * @return
	 */
	public List<EventCards> getEventsCards() {
		return eventsCards;
	}


	/**
	 * get crypt cards
	 * @return
	 */
	public List<CryptaCard> getCriptaCards() {
		return criptaCards;
	}

	/**
	 * get scriptorium cards
	 * @return
	 */

	public List<ScriptoriumCard> getScriptoriumCards() {
		return scriptoriumCards;
	}
	//</GETTERS>

	
	//<CREATORS>
	/**
	 * this will create the monk cards from XML
	 */
	private void createMonjes() {	
		monkList = new ArrayList<Monje>();
		try {
			 new ReadFromMonksXML(Constants.MonksFilePath);
		} catch (ParserConfigurationException | SAXException | IOException | NumberFormatException e) {
			System.err.println("ERROR: Error en el xml de los monjes");
			System.err.println(e);
			System.exit(1);	
		}
	}


	/**
	 * this will create Library  read from XML
	 * @return
	 */
	private List<LibraryCard> createLibraryCards() {
		try {
			ReadFromXML reader = new ReadFromXML(Constants.CartasBiblioteca);
			for(LibraryCard pibot: reader.card)
				System.err.println(pibot);
			return reader.card;
		} catch (ParserConfigurationException | SAXException | IOException | NumberFormatException e) {
			System.out.println("Error en el xml del mapa");
			System.out.println(e);
			System.exit(1);	
		}
		return null;
	}


	/**
	 * this will create Scriptorium read from XML
	 * @return
	 */
	private List<ScriptoriumCard> createScriptoriumCards() {
		try {
			ReadFromXML reader = new ReadFromXML(Constants.CartasScriptorium);
			for(ScriptoriumCard pibot: reader.cardS)
				System.err.println(pibot);
			return reader.cardS;
		} catch (ParserConfigurationException | SAXException | IOException | NumberFormatException e) {
			System.out.println("Error en el xml del mapa");
			System.out.println(e);
			System.exit(1);	
		}
		return null;
	}
	
	/**
	 * this will create event read from XML
	 * @return
	 */
	private List<EventCards> createEventCards() {
		try {
			ReadFromXML reader = new ReadFromXML(Constants.CartasEventos);
			for(EventCards pibot: reader.cardE)
				System.err.println(pibot);
			return reader.cardE;
		} catch (ParserConfigurationException | SAXException | IOException | NumberFormatException e) {
			System.out.println("Error en el xml del mapa");
			System.out.println(e);
			System.exit(1);	
		}
		return null;
	}
	
	
	/**
	 * this will create crypt read from XML
	 * @return
	 */
	private List<CryptaCard> createCriptaCards() {
		try {
			ReadFromXML reader = new ReadFromXML(Constants.CartasCripta);
			for(CryptaCard pibot: reader.cardC)
				System.err.println(pibot);
			List<CryptaCard> criptaCardsTemp = new ArrayList<CryptaCard>();
			CryptaCard criptaCard = reader.cardC.get(0);
			for(int i=1;i<=Constants.CRIPTA_CARDS_AMOUNT;i++) {
				criptaCardsTemp.add(criptaCard);
			}
			return criptaCardsTemp;
		} catch (ParserConfigurationException | SAXException | IOException | NumberFormatException e) {
			System.out.println("Error en el xml del mapa");
			System.out.println(e);
			System.exit(1);	
		}
		
		return null;
	}

	//</CREATORS>
	
	
	//<XML PARSERS>
	private class ReadFromXML extends DefaultHandler {

		private SAXParserFactory spfac;
		SAXParser sp;
		String tmpValue;
		List<LibraryCard> card = new ArrayList<LibraryCard>();
		List<ScriptoriumCard> cardS = new ArrayList<ScriptoriumCard>();
		LibraryCard cardPlay;
		boolean biblioteca;
		boolean scriptorium;
		ScriptoriumCard cardSPlay;
		List<EventCards> cardE = new  ArrayList<EventCards>();
		EventCards cardEPlay;
		CryptaCard cardCPLay;
		List<CryptaCard> cardC = new  ArrayList<CryptaCard>();
		boolean cripta=false;
		
		public ReadFromXML(String fichero) throws ParserConfigurationException, SAXException, IOException{
			spfac = SAXParserFactory.newInstance();
			//validador del dtd
			spfac.setValidating(true);
			 
			sp = spfac.newSAXParser();
			sp.parse(fichero, this);

		}

		public void startElement(String uri, String localName,String elementName, Attributes attributes) throws SAXException {
			if(elementName.equalsIgnoreCase("BibliotecaCard")){
				cardPlay = new LibraryCard();
				biblioteca=true;
				scriptorium= false;

			}			
			if(elementName.equalsIgnoreCase("scriptoriumCard")){
				cardSPlay = new ScriptoriumCard();
				biblioteca=false;
				scriptorium=true;

			}

			if(elementName.equalsIgnoreCase("EventosCard")){
				cardEPlay = new EventCards();
				biblioteca=false;
				scriptorium=false;

			}
			
			if(elementName.equalsIgnoreCase("CriptaCard")){
				cardCPLay = new CryptaCard();
				biblioteca=false;
				scriptorium=false;
				cripta=true;

			}
		}

		public void endElement(String s, String s1, String element) throws SAXException {


			if(element.equalsIgnoreCase("name")){
				if(cripta)cardCPLay.setCardName(tmpValue);
				else if(biblioteca)cardPlay.setCardName(tmpValue);
				else if(!biblioteca&&scriptorium)cardSPlay.setCardName(tmpValue);
				else if(!biblioteca&&!scriptorium) cardEPlay.setCardName(tmpValue);
				
			}
			if(element.equalsIgnoreCase("command")){
				if(cripta)cardCPLay.setCommand(tmpValue);
				else if(biblioteca)cardPlay.setCommand(tmpValue);
				else if(!biblioteca&&scriptorium) cardSPlay.setCommand(tmpValue);
				else if(!biblioteca&&!scriptorium)	cardEPlay.setCommand(tmpValue);
				
			}
					
			if(element.equalsIgnoreCase("id")){
				if(cripta)cardCPLay.setId(Integer.valueOf(tmpValue));
				else
				if(biblioteca)cardPlay.setId(Integer.valueOf(tmpValue));
				else if(!biblioteca&&scriptorium) cardSPlay.setId(Integer.valueOf(tmpValue));
				else if(!biblioteca&&!scriptorium)	cardEPlay.setId(tmpValue);
				
			}
			if(element.equalsIgnoreCase("texto")){
				if(cripta)cardCPLay.setTexto(tmpValue);
				else
				if(biblioteca)cardPlay.setTexto(tmpValue);
				else if(!biblioteca&&scriptorium) cardSPlay.setTexto(tmpValue);
				else if(!biblioteca&&!scriptorium)	cardEPlay.setTexto(tmpValue);
				
			}
			
			if(element.equalsIgnoreCase("special")){
			 if(scriptorium &&!tmpValue.equals(""))cardSPlay.setSpecial(tmpValue);

			}
			if(element.equalsIgnoreCase("param1")){
				if(cripta &&!tmpValue.equals(""))cardCPLay.setParam1(tmpValue);
				else
				if(biblioteca)if(!tmpValue.equals(""))cardPlay.setParam1(tmpValue);
				else if(scriptorium &&!tmpValue.equals(""))cardSPlay.setParam1(tmpValue);
				else if(!biblioteca && !scriptorium &&!tmpValue.equals(""))cardEPlay.setParam1(tmpValue);
				
			}
			if(element.equalsIgnoreCase("param2")){
				 if(cripta &&!tmpValue.equals(""))cardCPLay.setParam2(tmpValue);
				 else
					 if(biblioteca)if(!tmpValue.equals(""))cardPlay.setParam2(tmpValue);
				else if(scriptorium &&!tmpValue.equals(""))cardSPlay.setParam2(tmpValue);
				else if(!biblioteca&&!scriptorium &&!tmpValue.equals(""))cardEPlay.setParam2(tmpValue);
				
			}
			if(element.equalsIgnoreCase("BibliotecaCard")){
				card.add(cardPlay);
			}
			if(element.equalsIgnoreCase("scriptoriumCard")){
				cardS.add(cardSPlay);
			}

			if(element.equalsIgnoreCase("EventosCard")){
				cardE.add(cardEPlay);
			}
			
			if(element.equalsIgnoreCase("Cripta")){
				cardC.add(cardCPLay);
			}
			
			if(element.equalsIgnoreCase("Biblioteca")){
				this.endDocument();
			}
			if(element.equalsIgnoreCase("Scriptorium")){
				this.endDocument();
			}
			if(element.equalsIgnoreCase("Eventos")){
				this.endDocument();
			}
			if(element.equalsIgnoreCase("Cripta")){
				this.endDocument();
			}

		}

		public void characters(char[] ac, int i, int j) throws SAXException {
			tmpValue = new String(ac, i, j);
		}	
	}//END PRIVATE CLASS

	private class ReadFromMonksXML extends DefaultHandler {

		private SAXParserFactory spfac;
		private SAXParser sp;
		private Monje monkTmp;
		private String monkName;
		private String tmpValue;
		private int monkId;
		private List<Features> featuresList;
		private Features feature;

		public ReadFromMonksXML(String fichero) throws ParserConfigurationException, SAXException, IOException {
			spfac = SAXParserFactory.newInstance();
			sp = spfac.newSAXParser();
			sp.parse(fichero, this);
		}

		public void startElement(String uri, String localName,String elementName, Attributes attributes) throws SAXException {

			if(elementName.equalsIgnoreCase("features")) featuresList = new ArrayList<Features>();

		}

		public void endElement(String s, String s1, String element) throws SAXException {

			if(element.equalsIgnoreCase("name")) monkName = tmpValue;

			else if(element.equalsIgnoreCase("id")) monkId = Integer.parseInt(tmpValue);

			else if(element.equalsIgnoreCase("Title")) {
				feature = Features.Titulo.valueOf(tmpValue);
				featuresList.add(feature);
			}

			else if(element.equalsIgnoreCase("Order")) {
				feature = Features.Orden.valueOf(tmpValue);
				featuresList.add(feature);
			}

			else if(element.equalsIgnoreCase("Hood")) {
				feature = Features.Cabeza.valueOf(tmpValue);
				featuresList.add(feature);
			}

			else if(element.equalsIgnoreCase("Girth")) {
				feature = Features.Cuerpo.valueOf(tmpValue);
				featuresList.add(feature);
			}

			else if(element.equalsIgnoreCase("FacialHair")){
				feature = Features.VelloFacial.valueOf(tmpValue);
				featuresList.add(feature);
			}

			else if(element.equalsIgnoreCase("monk")){
				monkTmp = new Monje(monkName, monkId, featuresList);
				monkList.add(monkTmp);
			}

			else if(element.equalsIgnoreCase("MONKS")){
				this.endDocument();
			}

		}

		public void characters(char[] ac, int i, int j) throws SAXException {
			tmpValue = new String(ac, i, j);
		}	
	}//END PRIVATE CLASS
	//</XML PARSERS>

}
